import { Injectable } from "@angular/core";
import { Observable, of as ObserveOf, throwError } from "rxjs";
import * as _ from 'lodash';

@Injectable()
export class UtilService {
  defaultValue(a: any, b: any): any {
    if (a === null || a === undefined) {
      return b;
    }
    return a;
  }

  /**
   * 转换数组为树形结构
   *
   * @param data
   * @param ops
   * @returns
   */
  flat2Nested<T>(
    data: Array<T>,
    options: {
      idProperty?: string;
      pathProperty?: string;
      parentIdProperty?: string;
      sortProperty?: string;
      childrenProperty?: string;
      rootId: any;
      pathSeparator?: string;
      filterProperties?: Array<string>;
      filterValues?: Array<string>;
      depth?: number;
    }
  ): Observable<T> {
    const ops = {
      ...{
        idProperty: "id",
        pathProperty: "path",
        parentIdProperty: "parentId",
        childrenProperty: "children",
        pathSeparator: ",",
        depth: -1
      },
      ...options
    };

    // 参数错误判断
    const error = "树转换错误(from UtilService.flat2Nested)：";
    if (data.length <= 0) {
      console.error(`数据不应为空: \n${error}`);
      return throwError(new Error(`数据不应为空: \n${error}`));
    }
    // 数据预处理
    let newData: Array<T> = JSON.parse(JSON.stringify(data));
    const rootItem = newData.filter(item => {
      return item[ops.idProperty] === ops.rootId;
    })[0];
    console.log(rootItem);
    if (!rootItem) {
      console.error(`无法找到指定根节点: \n${error}`);
      return throwError(new Error(`无法找到指定根节点: \n${error}`));
    }
    const rootLevel = rootItem[ops.pathProperty].split(ops.pathSeparator)
      .length;
    if (ops.depth > 0) {
      newData = newData.filter(item => {
        const level = item[ops.pathProperty].split(ops.pathSeparator).length;
        let res = level > rootLevel;
        if (ops.depth > 0) {
          res = res || level <= rootLevel + ops.depth;
        }
        return res;
      });
    }
    // 构建树
    const result = rootItem;
    const temp = {};
    temp[ops.rootId] = rootItem;
    newData.sort((a, b) => {
      return (
        a[ops.pathProperty].split(ops.pathSeparator).length -
        b[ops.pathProperty].split(ops.pathSeparator).length
      );
    });
    while (newData.length > 0) {
      const item = newData[0];
      const parentItem = temp[item[ops.parentIdProperty]];
      if (parentItem) {
        if (!parentItem.children) {
          parentItem.children = new Array<object>();
        }
        parentItem.children.push(item);
        temp[item[ops.idProperty]] = item;
      }
      newData.shift();
    }
    return ObserveOf(result);
  }

  getItemValues(list, key, isNumber?, digit?) {
    return list.map(i => {
      const value = i[key] || 0;
      if (isNumber) {
        return this.transformValue(value, digit);
      } else {
        return value || "其他";
      }
    });
  }

  getTimeItems(list, key) {
    return list.map(i => {
      return i[key].slice(5, -3);
    });
  }

  transformValue(value, digit?) {
    if (`${value}`.split("/").length > 1) {
      return `${value}`.split("/").reduce((def, i) => {
        def += parseFloat(i);
        return def;
      }, 0);
    } else {
      if (digit) {
        value = Number(value).toFixed(digit);
      }
      return parseFloat(value) || 0;
    }
  }

  getScatterMapper(list) {
    return list.reduce((mapper, item) => {
      if (mapper[item]) {
        mapper[item]++;
      } else {
        mapper[item] = 1;
      }
      return mapper;
    }, {});
  }

  getTop5Mapper(mapper) {
    const arr = _.toPairs(mapper);
    arr.sort((a: [string, number], b: [string, number]) => {
      return b[1] - a[1];
    });
    const obj: { [key: string]: any } = _.fromPairs(_.take(arr, 5));
    if (_.slice(arr, 5).length) {
      obj['其他'] = obj['其他'] || 0;
      obj['其他'] += this.sum(Object.values(_.fromPairs(_.slice(arr, 5))));
    }
    console.log(obj);
    return obj;
  }
  getScatterData(mapper) {
    return [
      {
        symbolSize: 8,
        emphasis: {
          label: {
            show: false,
            formatter: param => {
              return `${param.data[0]}`;
            },
            position: "top"
          }
        },
        data: Object.keys(mapper).map(key => [key, mapper[key]])
      }
    ];
  }

  max(list) {
    return Math.max.apply(null, list);
  }

  min(list) {
    return Math.min.apply(null, list);
  }

  sum(list) {
    const sum = list.reduce((def, val) => {
      def += parseFloat(val || '0');
      return def;
    }, 0);
    return Math.ceil(sum * 100) / 100;
  }

  average(list) {
    return Math.floor((this.sum(list) * 100) / list.length) / 100;
  }

  averageExceptNull(list) {
    const listFilter = _.without(list, "");
    return Math.floor((this.sum(list) * 100) / listFilter.length) / 100;
  }

  median(list) {
    return parseFloat(list[Math.floor(list.length / 2)]);
  }
}
